/*
 * Copyright (c) 2024 iSOFT INFRASTRUCTURE SOFTWARE CO., LTD.
 * easyAda is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */


#include <tools/list.h>
#include <tools/macros.h>
#include <core/notification.h>
#include <core/schedule.h>
#include <uapi/errors.h>

/* PRQA S 0570, 4424 EOF # allow enum as unsigned char */
/* PRQA S 2890, 4521 EOF # negative value for retcode */

void notification_init(notification_t *ntfn)
{
    list_init(&ntfn->waiters);
    notification_set_state(ntfn, NTFN_IDLE);
    notification_init_count(ntfn);
    object_init(&ntfn->obj);
}

static inline void notification_enque(notification_t *ntfn, tcb_t *task)
{
    tcb_get(task);

    schedule_detach(task);

    task_set_state(task, TASK_BLOCKED);
    list_append(&ntfn->waiters, &task->runlist);
}

static inline int notification_deque(tcb_t *task)
{
    list_remove(&task->runlist);
    task_set_state(task, TASK_READY);

    return tcb_put(task);
}

void notification_wait(notification_t *ntfn, tcb_t *receiver, int block)
{
    switch (ntfn->state) {
        case NTFN_WAIT:
        case NTFN_IDLE:
            if (block == 0) {
                task_set_retcode(receiver, ERR_TO_RETCODE(-EAGAIN));
            } else {
                notification_enque(ntfn, receiver);
                ntfn->state = NTFN_WAIT;
            }
            break;

        case NTFN_ACTIVE:
            ntfn->count--;
            task_set_retcode(receiver, ESUCCESS);
            if (ntfn->count == 0U) {
                notification_set_state(ntfn, NTFN_IDLE);
            }
            break;

        default:
            task_set_retcode(current(), ERR_TO_RETCODE(-EINVALID));
            break;
    }
}

void notification_signal(notification_t *ntfn, tcb_t *sender)
{
    tcb_t *receiver;

    if (sender != NULL) {
        task_set_retcode(sender, ESUCCESS);
    }

    switch (ntfn->state) {
        case NTFN_ACTIVE:
        case NTFN_IDLE:
            ntfn->count++;
            ntfn->state = NTFN_ACTIVE;
            break;

        case NTFN_WAIT:
            assert(!list_is_empty(&ntfn->waiters));

            receiver = container_of(ntfn->waiters.next, tcb_t, runlist); /* PRQA S 3432 */
            if (notification_deque(receiver) == 0) {
                return;
            }

            task_set_retcode(receiver, ESUCCESS);
            schedule_attach(receiver);

            if (list_is_empty(&ntfn->waiters)) {
                ntfn->state = NTFN_IDLE;
            }
            break;

        default:
            task_set_retcode(sender, ERR_TO_RETCODE(-EINVALID));
            break;
    }
}

void notification_cancel(notification_t *ntfn)
{
    tcb_t *waiter;

    while (list_is_empty(&ntfn->waiters)) {
        waiter = container_of(ntfn->waiters.next, tcb_t, runlist); /* PRQA S 3432 */
        if (notification_deque(waiter) == 0) {
            continue;
        }

        task_set_retcode(waiter, ERR_TO_RETCODE(-ECANCELED));
    }

    notification_set_state(ntfn, NTFN_DIED);
}
